// 平滑滚动公式：easeInOutQuad
function easeInOutQuad(t: number, b: number, c: number, d: number): number {
  t /= d / 2
  if (t < 1) return (c / 2) * t * t + b
  t--
  return (-c / 2) * (t * (t - 2) - 1) + b
}

// 浏览器兼容的 requestAnimationFrame
const requestAnimFrame =
  window.requestAnimationFrame ||
  window.webkitRequestAnimationFrame ||
  window.mozRequestAnimationFrame ||
  function (callback: FrameRequestCallback) {
    return window.setTimeout(callback, 1000 / 60)
  }

// 获取当前滚动位置
function getScrollTop(): number {
  return document.documentElement.scrollTop || document.body.parentElement?.scrollTop || document.body.scrollTop || 0
}

// 设置滚动位置
function setScrollTop(value: number): void {
  document.documentElement.scrollTop = value
  if (document.body.parentElement) {
    document.body.parentElement.scrollTop = value
  }
  document.body.scrollTop = value
}

/**
 * 页面滚动动画
 * @param to 目标滚动位置（px）
 * @param duration 滚动时间（ms），默认 500ms
 * @param callback 滚动完成回调
 */
export function scrollTo(to: number, duration = 500, callback?: () => void): void {
  const start = getScrollTop()
  const change = to - start
  const increment = 20
  let currentTime = 0

  function animateScroll() {
    currentTime += increment
    const val = easeInOutQuad(currentTime, start, change, duration)
    setScrollTop(val)
    if (currentTime < duration) {
      requestAnimFrame(animateScroll)
    } else {
      callback?.()
    }
  }

  animateScroll()
}

/**
 * 元素内部滚动动画
 * @param el 要滚动的 DOM 元素
 * @param to 滚动到的位置
 * @param duration 动画持续时间（ms），默认 500ms
 * @param callback 滚动完成后执行的回调函数
 */
export function scrollToDom(el: HTMLElement, to: number, duration = 500, callback?: () => void): void {
  const start = el.scrollTop
  const change = to - start
  const increment = 20
  let currentTime = 0

  function animateScroll() {
    currentTime += increment
    el.scrollTop = easeInOutQuad(currentTime, start, change, duration)
    if (currentTime < duration) {
      requestAnimFrame(animateScroll)
    } else {
      callback?.()
    }
  }

  animateScroll()
}
